package com.gitee.faster.redis.protocol;

import cn.hutool.core.collection.CollUtil;
import com.gitee.faster.redis.command.CommandPool;
import com.gitee.faster.redis.command.RedisCommand;
import org.smartboot.socket.MessageProcessor;
import org.smartboot.socket.transport.AioSession;
import redis.clients.jedis.args.Rawable;
import redis.clients.jedis.util.RedisOutputStream;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static redis.clients.jedis.Protocol.ASTERISK_BYTE;
import static redis.clients.jedis.Protocol.DOLLAR_BYTE;

/**
 * @author hongda.li
 */
@SuppressWarnings("ReassignedVariable")
public class RedisProcessor implements MessageProcessor<RedisRequest> {

    private final CommandPool commandPool;

    public RedisProcessor() {
        this.commandPool = new CommandPool();
        this.commandPool.scan(RedisCommand.class);
    }

    @Override
    public void process(AioSession session, RedisRequest request) {
        RedisResponse response = this.handle(request);
        RedisOutputStream os = new RedisOutputStream(session.writeBuffer());
        try {
            os.write(ASTERISK_BYTE);
            os.writeIntCrLf(response.size());
            for (Rawable arg : response) {
                os.write(DOLLAR_BYTE);
                final byte[] bin = arg.getRaw();
                os.writeIntCrLf(bin.length);
                os.write(bin);
                os.writeCrLf();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public RedisResponse handle(RedisRequest request) {
        RedisResponse response = new RedisResponse();
        List<String> dataList = request.getDataList();

        if (CollUtil.isEmpty(dataList)) {
            return response.addItem("Empty command");
        }

        int size = dataList.size();
        for (int current = 0; current < size; current++) {
            String data = dataList.get(current);
            RedisCommand command = this.commandPool.search(data);

            List<String> arguments = new ArrayList<>();
            if (command != null) {
                int nextCommandIndex = current + 1;
                while (nextCommandIndex < size) {
                    String next = dataList.get(nextCommandIndex);
                    boolean noneMatch = this.commandPool.search(next) == null;
                    if (noneMatch) {
                        arguments.add(next);
                        nextCommandIndex++;
                    } else {
                        break;
                    }
                }
                current = nextCommandIndex - 1;
                response.addItems(command.execute(request, arguments));
            }
        }

        return response;
    }
}
